﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace DbView
{
	class PluginLoader
	{
		//////////////////////////////////////////////////////////////
		// scans current assenbly plus all other assemblies in the same
		// location for objects that implement the target interface
		// then creates an object of each.
		// Objects need to have a parameterless constructor.
		//
		// PluginInterface must be fully qualified with the namespace
		public static List<Object> GetPluginObjects(String PluginInterface)
		{
			// use an object - makes refactoring easier
			PluginLoader Loader = new PluginLoader();

			// check main program for objects
			Assembly assem = Assembly.GetExecutingAssembly();
			Loader.AddFromAssembly(assem, PluginInterface);

			FileInfo[] pluginAssemblies = Loader.GetOtherAssemblies(assem.Location);
			foreach (FileInfo fi in pluginAssemblies)
			{
				assem = Assembly.LoadFile(fi.FullName);
				Loader.AddFromAssembly(assem, PluginInterface);
			}
			return Loader.PluginList;
		}

		//////////////////////////////////////////////////////////////
		// v2. scans current assenbly plus all other assemblies in the same
		// location for objects that implement the target interface
		// then creates an object of each.
		// Objects need to have a parameterless constructor.
		//
		// PluginInterface must be fully qualified with the namespace
		public static void LoadPlugins(String PluginInterface)
		{
			// use an object - makes refactoring easier
			PluginLoader Loader = new PluginLoader();

			// check main program for objects
			Assembly assem = Assembly.GetExecutingAssembly();
			Loader.LoadFromAssembly(assem, PluginInterface);

			FileInfo[] pluginAssemblies = Loader.GetOtherAssemblies(assem.Location);
			foreach (FileInfo fi in pluginAssemblies)
			{
				assem = Assembly.LoadFile(fi.FullName);
				Loader.LoadFromAssembly(assem, PluginInterface);
			}
		}

		private FileInfo[] GetOtherAssemblies(String mainProgram)
		{
			string p = Path.GetDirectoryName(mainProgram);
			DirectoryInfo di = new DirectoryInfo(p);
			FileInfo[] pluginAssemblies = di.GetFiles("*.dll");
			return pluginAssemblies;
		}

		public void AddFromAssembly(Assembly assem, string PluginInterface)
		{
			Type[] typeArray = assem.GetTypes();
			// Walk through each type in the assembly
			foreach (Type type in typeArray)
			{
				// Pick up a class
				if (type.IsClass == true)
				{
					// Console.WriteLine ("...Found Class : {0}", type.FullName);
					// If it does not implement the IBase Interface, skip it
					if (type.GetInterface(PluginInterface) != null)
					{
						// activate 
						Object ibaseObject = Activator.CreateInstance(type);
						this.m_PluginList.Add(ibaseObject);
					}
				}
			}
		}

		public void LoadFromAssembly(Assembly assem, string PluginInterface)
		{
			Type[] typeArray = assem.GetTypes();
			// Walk through each type in the assembly
			foreach (Type type in typeArray)
			{
				// Pick up a class
				if (type.IsClass == true)
				{
					// Console.WriteLine ("...Found Class : {0}", type.FullName);
					// If it does not implement the IBase Interface, skip it
					if (type.GetInterface(PluginInterface) != null)
					{
						// activate 
						Object ibaseObject = Activator.CreateInstance(type);
						PluginManager.Instance.AddPlugin(PluginInterface, ibaseObject);
					}
				}
			}
		}

		public List<Object> PluginList { get { return this.m_PluginList; } }
		List<Object> m_PluginList = new List<object>();

	}

	///////////////////////////////////////////////////////////////////////////////
	// Singleton plugin Resource file
	//
	public class PluginManager
	{

		public static PluginManager Instance { get { return _Instance; } }

		public void AddPlugin(String InterfaceTypeName, Object plugObject)
		{
			List<Object> plugins;
			if (!this.m_Heap.ContainsKey(InterfaceTypeName))
			{
				plugins = new List<object>();
				this.m_Heap.Add(InterfaceTypeName, plugins);
			}
			else
				plugins = this.m_Heap[InterfaceTypeName];
			plugins.Add(plugObject);
		}

		public List<object> GetPlugins(String InterfaceTypeName)
		{
			if (this.m_Heap.ContainsKey(InterfaceTypeName))
				return this.m_Heap[InterfaceTypeName];
			// no available plugins of the requested type
			// so return an empty list
			return new List<object>();
		}

		// the instance
		static PluginManager _Instance = new PluginManager();
		// the data
		Dictionary<String, List<object>> m_Heap = new Dictionary<string, List<object>>();
	}

}
